/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Sylvain Sicard - initial API and implementation
 ******************************************************************************/
package fr.jade.fraclite.factory;

import java.lang.reflect.Proxy;
import java.rmi.dgc.VMID;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.Type;
import org.objectweb.fractal.api.control.BindingController;
import org.objectweb.fractal.api.factory.GenericFactory;
import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.api.type.ComponentType;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.fractal.api.type.TypeFactory;

import fr.jade.fraclite.GenericComponentInterface;
import fr.jade.fraclite.InitializationContext;
import fr.jade.fraclite.api.ComponentInterface;
import fr.jade.fraclite.api.Controller;

/**
 * Provides a basic implementation of the {@link GenericFactory} interface.
 * <br>
 * <br>
 * <b>Requirements</b>
 * <ul>
 * <li>the types used in the {@link #newFcInstance newFcInstance} method must
 * be instances of {@link ComponentType}.</li>
 * </ul>
 */

public class BasicGenericFactory implements Controller, GenericFactory {

  protected ClassLoader loader;
  protected static long itfCounter = 1;
  
  // -------------------------------------------------------------------------
  // PUBLIC constructor and weaveableTF are needed for bootstrap
  // -------------------------------------------------------------------------

  public BasicGenericFactory () {
    loader = this.getClass().getClassLoader();
    String vmId = System.getProperty("fraclite.ha.vmid");
    //if (vmId==null) vmId=new VMID().toString();
    if (vmId==null) vmId=String.valueOf(Math.round(Math.random()*10000));
    itfCounter = Long.parseLong(vmId)*10000000;
  }
  
  public TypeFactory weaveableTF;
  
  protected MembraneType mt = null;
  
  public void initFcController (final InitializationContext ic) throws InstantiationException {
    weaveableTF = (TypeFactory) ic.getInterface("type-factory");
  }
  
  public Component newFcInstance (final Type type, final Object controllerDesc, final Object contentDesc) throws InstantiationException {
    if(mt == null) mt = new MembraneType(weaveableTF);
    InitializationContext ic = new InitializationContext();
    ic.type = type;
    
    String controllerDescLabel;
//    if(controllerDesc instanceof Map) {
//      Object o = ((Map<?,?>)controllerDesc).get("controllerDescLabel");
//      assert o instanceof String : "controllerDescLabel must be instanceof String";
//      controllerDescLabel = (String)o;
//      o = ((Map<?,?>)controllerDesc).get("componentId");
//      if(o == null){
//        ic.cmpId = getComponentId();
//      }else{
//        assert o instanceof Long : "componentId must be instanceof Long";
//        ic.cmpId = ((Long)o)*100;
//      }
//    } else {
      assert controllerDesc instanceof String : "controllerDesc must be instance of String";
      controllerDescLabel = (String) controllerDesc;
//      ic.cmpId = getComponentId();
//    }
    
    createContent(contentDesc, ic);
    createMembrane(type, controllerDescLabel, ic);
    initControllers(ic);
    initContent(ic);
    return (Component)ic.getInterface("component");
  }

  protected void initContent(InitializationContext ic) throws InstantiationException {
    if (ic.content != null){
      if(ic.content instanceof BindingController){
        Object cmp = ic.getInterface("component");
        try {
          ((BindingController)ic.content).bindFc("component", cmp);
        } catch (Exception e) {
          throw new ChainedInstantiationException(e, "Can't initialize component content");
        }
      }
    }
  }

  protected void initControllers(InitializationContext ic) throws InstantiationException {
    Component cmp = (Component)ic.getInterface("component");
    ((ComponentInterface)cmp).setFcItfGUId(getItfId());
    for(Interface controller : ic.controllers){
      ((ComponentInterface)controller).setFcItfGUId(getItfId());
      ((ComponentInterface)controller).setFcItfOwner(cmp);
      Controller ctrlImpl = (Controller) ((ComponentInterface)controller).getFcItfImpl();
      ctrlImpl.initFcController(ic);
    }
    for(Interface itf : ic.interfaces.values()){
      ((ComponentInterface)itf).setFcItfGUId(getItfId());
      ((ComponentInterface)itf).setFcItfOwner(cmp);
    }
    for(Interface itf : ic.internalInterfaces.values()){
      ((ComponentInterface)itf).setFcItfGUId(getItfId());
      ((ComponentInterface)itf).setFcItfOwner(cmp);
    }
  }
  
//  protected long getComponentId() {
//	return System.nanoTime()*100;
//  }
  
  protected long getItfId(){
    return itfCounter++;
  }

  protected void createContent(Object contentDesc, InitializationContext ic) throws InstantiationException {
    assert contentDesc == null || contentDesc instanceof String :"contentDesc must be instance of String";
  
    if(contentDesc == null){
      ic.content = null;
    }else{
      ic.content = newImplObject((String) contentDesc);
    }
  }

  protected void createMembrane(Type componentType, String controllerDesc, InitializationContext ic) throws InstantiationException {
    assert componentType instanceof ComponentType :"type must be instance of ComponentType";
    
    String itfImplClass = null;
    // create controllers
    ComponentType ct = mt.getControlType(controllerDesc);
    for(InterfaceType it : ct.getFcInterfaceTypes()){
      itfImplClass = mt.getControlImpl(controllerDesc, it);
      Object ctrlImpl = newImplObject(itfImplClass);
      Interface c = (Interface) newInterfaceObject(it, false, ctrlImpl);
      ic.controllers.add(c);
      ic.interfaces.put(it.getFcItfName(),c);
    }
    // create functional interfaces
    for(InterfaceType it : ((ComponentType)componentType).getFcInterfaceTypes()){
      Interface itf = (Interface) newInterfaceObject(it, false, ic.content);
      ic.interfaces.put(it.getFcItfName(),itf);
      InterfaceType complementaryItfType = getComplementaryInterfaceType(it);
      itf = (Interface) newInterfaceObject(complementaryItfType, true, null);
      ic.internalInterfaces.put(it.getFcItfName(), itf);
    }
  }

  protected InterfaceType getComplementaryInterfaceType(InterfaceType itfType) throws InstantiationException {
    return weaveableTF.createFcItfType( itfType.getFcItfName(),
                                        itfType.getFcItfSignature(),
                                        !itfType.isFcClientItf(),
                                        itfType.isFcOptionalItf(),
                                        itfType.isFcCollectionItf()
                                       );
  }

  private Object newImplObject(String contentDesc) throws InstantiationException {
    try {
      Class<?> contentClass = loader.loadClass((String)contentDesc);
      return contentClass.newInstance();
    } catch (ClassNotFoundException e) {
      throw new ChainedInstantiationException(e,"Content class not found: " + contentDesc);
    } catch (java.lang.InstantiationException e) {
      throw new ChainedInstantiationException(e,"InstantiationException: (" + contentDesc + ")");
    } catch (IllegalAccessException e) {
      throw new ChainedInstantiationException(e,"IllegalAccessException: (" + contentDesc + ")");
    }
  }

  protected Object newInterfaceObject(InterfaceType type, boolean isInternal, Object impl) throws InstantiationException {
    try {
      Class<?> itfClass = loader.loadClass(type.getFcItfSignature());
      Class<?> bciClass = ComponentInterface.class;
      Class<?>[] itfs = new Class[] {bciClass, itfClass};
      return Proxy.newProxyInstance(loader, itfs, new GenericComponentInterface(type.getFcItfName(),type,isInternal,impl));
    } catch (ClassNotFoundException e) {
      throw new InstantiationException("Controller interface class not found: " + type.getFcItfSignature());
    }
    
  }

}
